/*****************************************************************
Copyright (c) 2011 - 2016 Qualcomm Technologies International, Ltd.

PROJECT
    source

FILE NAME
    source_usb.c

DESCRIPTION
    USB functionality.

*/


/* header for this file */
#include "source_usb.h"
/* application header files */
#include "source_app_msg_handler.h"
#include "source_debug.h"
#include "source_memory.h"
#include "source_private.h"
#include "source_audio.h"
#include "source_led_handler.h"
#include "source_aghfp.h"
#include "source_buttons.h"
#include "source_avrcp.h"
#include "source_connection_mgr.h"
#include "source_upgrade.h"
#include "source_ps.h"
#include "source_call_control.h"
#include "source_volume.h"
#include "system.h"
/* VM headers */
#include <boot.h>
#include <panic.h>
#include <pio_common.h>
#include <sink.h>
#include <source.h>
#include <stream.h>
#include <string.h>
#include <system_reboot.h>
#include <stdlib.h>
#include <usb.h>
#include <vmal.h>
#include <stdio.h>
#include <hid_upgrade.h>
#include <loader.h>
#include <usb_hub.h>


#ifdef DEBUG_USB
    #define USB_DEBUG(x) SOURCE_DEBUG(x)
#else
    #define USB_DEBUG(x)
#endif

/** Most significant byte in word */
#define MSB(x)                          ((uint8)(((x) >> 8) & 0xFF))
/** Least significant byte in word */
#define LSB(x)                          ((uint8)(((x) >> 0) & 0xFF))
#define USB_REQ_TYPE_STANDARD     	    0x00
#define USB_REQ_TYPE_CLASS        	    0x20
#define USB_REQ_TYPE_VENDOR       	    0x40
#define USB_REQ_TYPE_MASK         	    0x60

#define SPEAKER_VOLUME_MAX              0x0500
#define SPEAKER_VOLUME_MIN              0xD800
#define SPEAKER_VOLUME_RES              0x0300
#define SPEAKER_VOLUME_DEFAULT          0x0100

#define HEADSET_VOLUME_DEFAULT          12

#define MIC_VOLUME_MAX                  0x0900
#define MIC_VOLUME_MIN                  0xF400
#define MIC_VOLUME_RES                  0x0300
#define MIC_VOLUME_DEFAULT              0x0300

#define B_DESCRIPTOR_TYPE_CS_INTERFACE  0x24
#define B_DESCRIPTOR_SUB_TYPE_FORMAT    0x02
#define B_DESCRIPTOR_FORMAT_TYPE_I      0x01

#define B_INTERFACE_CLASS_AUDIO 0x01
#define B_INTERFACE_SUB_CLASS_AUDIOCONTROL   0x01
#define B_INTERFACE_SUB_CLASS_AUDIOSTREAMING 0x02
#define PR_PROTOCOL_UNDEFINED 0x00

#define B_INTERFACE_CLASS_HID 0x03
#define B_INTERFACE_SUB_CLASS_HID_NO_BOOT 0x00
#define B_INTERFACE_PROTOCOL_HID_NO_BOOT 0x00

#define I_INTERFACE_INDEX 0x00

#define UCQ_STRING_INDEX 33

const uint16 UsbUcqString[] = {'U','C','Q','0','0','0','1','1','0','0','0','1','0','1','0','0','0', 0};

typedef enum
{
    HOST_OS_UNKNOWN = 0,     /**< Unknown host */
    HOST_OS_WINDOWS_7,       /**< Windows 7/8/10 host */
    HOST_OS_WINDOWS_XP,      /**< Windows 9X/2K/XP host */
	HOST_OS_PS4,             /**< Sony Play Station */
    HOST_OS_LINUX,           /**< Linux - Ubuntu/Fedora host */
    HOST_OS_MAC              /**< Mac - OSX host */
}OS_type;

#ifdef INCLUDE_USB_AUDIO
static const UsbCodes usb_codes_ac = {B_INTERFACE_CLASS_AUDIO, /* bInterfaceClass */
                                      B_INTERFACE_SUB_CLASS_AUDIOCONTROL, /* bInterfaceSubClass */
                                      PR_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */
                                      I_INTERFACE_INDEX /* iInterface */
                                      };

static const UsbCodes usb_codes_as = {B_INTERFACE_CLASS_AUDIO, /* bInterfaceClass */
                                      B_INTERFACE_SUB_CLASS_AUDIOSTREAMING, /* bInterfaceSubClass */
                                      PR_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */
                                      I_INTERFACE_INDEX /* iInterface */
                                      };
#endif

static const UsbCodes usb_codes_hid_no_boot = {B_INTERFACE_CLASS_HID, /* bInterfaceClass */
                                               B_INTERFACE_SUB_CLASS_HID_NO_BOOT, /* bInterfaceSubClass */
                                               B_INTERFACE_PROTOCOL_HID_NO_BOOT, /* bInterfaceProtocol */
                                               I_INTERFACE_INDEX /* iInterface */
                                               };

static const int16 SpeakerVolumeTableWindowsIn[] =
{
  -10240,
  -6926,
  -5163,
  -3954,
  -3033,
  -2462,
  -1811,
  -1254,
  -766,
  -437,
  -37,
  252,
  565,
  789,
  1027
  //1280
};

#if 0
static const int16 SpeakerVolumeTableWin7Out[17] =
{
  -10240,
  -7528,
  -5734,
  -4515,
  -3702,
  -2932,
  -2288,
  -1811,
  -1586,
  -942,
  -598,
  -282,
  56,
  354,
  676,
  1027,
  1280
};
#endif

/* HID Report Descriptor - Consumer Transport Control Device */
static const uint8 usb_report_descriptor_hid_consumer_transport[] =
{
#ifdef INCLUDE_TEAMS
    0x05, 0x0B,          /* USAGE_PAGE (Telephony) */
    0x09, 0x05,          /* USAGE (Headset) */
    0xA1, 0x01,          /* COLLECTION (Application) */
    0x85, 0x09,          /*   Report Id (9)*/
    0x05, 0x0B,			 /*   Usage Page (Telephony)*/
    0x15, 0x00,			 /*   Logical minimum (0)*/
    0x25, 0x01,			 /*   Logical maximum (1)*/
    0x09, 0x20,			 /*   Usage (Hook Switch)*/
    0x75, 0x01,			 /*   Report Size (1)*/
    0x95, 0x01,			 /*   Report Count (1)*/
    0x81, 0x22,			 /*   Input (Data,Value,Absolute,Bit Field)*/
    0x09, 0x2F,			 /*   Usage (Phone Mute)*/
    0x09, 0x21,			 /*   Usage (Flash)*/
    0x09, 0x24,			 /*   Usage (Redial)*/
    0x75, 0x01,			 /*   Report Size (1)*/
    0x95, 0x03,			 /*   Report Count (3)*/
    0x81, 0x06,			 /*   Input (Data,Value,Relative,Bit Field)*/
    0x75, 0x01,			 /*   Report Size (1)*/
    0x95, 0x0C,			 /*   Report Count (12)*/
    0x81, 0x01,			 /*   Input (Constant,Array,Absolute,Bit Field)*/
    0x05, 0x08,			 /*   Usage Page (LEDs)*/
    0x15, 0x00,			 /*   Logical minimum (0)*/
    0x25, 0x01,			 /*   Logical maximum (1)*/
    0x09, 0x17,			 /*   Usage (Off-Hook)*/
    0x09, 0x09,			 /*   Usage (Mute)*/
    0x09, 0x18,			 /*   Usage (Ring)*/
    0x09, 0x20,			 /*   Usage (Hold)*/
    0x09, 0x21,			 /*   Usage (Microphone)*/
    0x09, 0x2A,			 /*   Usage (On-Line)*/
    0x75, 0x01,			 /*   Report Size (1)*/
    0x95, 0x06,			 /*   Report Count (6)*/
    0x91, 0x22,			 /*   Output (Data,Value,Absolute,Non-volatile,Bit Field)*/
    0x05, 0x0B,			 /*   Usage Page (Telephony)*/
    0x15, 0x00,			 /*   Logical minimum (0)*/
    0x25, 0x01,			 /*   Logical maximum (1)*/
    0x09, 0x9E,			 /*   Usage (Ringer)*/
    0x75, 0x01,			 /*   port Size (1)*/
    0x95, 0x01,			 /*   Report Count (1)*/
    0x91, 0x22,			 /*   Output (Data,Value,Absolute,Non-volatile,Bit Field)*/
    0x75, 0x01,			 /*   Report Size (1)*/
    0x95, 0x09,			 /*   Report Count (9)*/
    0x91, 0x01,			 /*   Output (Constant,Array,Absolute,Non-volatile,Bit Field)*/
    0xC0,                /* END_COLLECTION */
#endif
    0x05, 0x0C,                  /* USAGE_PAGE (Consumer Devices) */
    0x09, 0x01,                  /* USAGE (Consumer Control) */
    0xa1, 0x01,                  /* COLLECTION (Application) */
    0x85, 0x07,                  /*   REPORT_ID (7) */
    0x15, 0x00,                  /*   LOGICAL_MINIMUM (0) */
    0x25, 0x01,                  /*   LOGICAL_MAXIMUM (1) */
    0x09, 0xcd,                  /*   USAGE (Play/Pause) */
    0x09, 0xb5,                  /*   USAGE (Scan Next Track) */
    0x09, 0xb6,                  /*   USAGE (Scan Previous Track) */
    0x09, 0xb7,                  /*   USAGE (Stop) */
    0x75, 0x01,                  /*   REPORT_SIZE (1) */
    0x95, 0x04,                  /*   REPORT_COUNT (4) */
    0x81, 0x02,                  /*   INPUT (Data,Var,Abs,Bit Field) */
    0x15, 0x00,                  /*   LOGICAL_MINIMUM (0) */
    0x25, 0x01,                  /*   LOGICAL_MAXIMUM (1) */
    0x09, 0xb0,                  /*   USAGE (Play) */
    0x09, 0xb1,                  /*   USAGE (Pause) */
    0x09, 0xb3,                  /*   USAGE (Fast Forward) */
    0x09, 0xb4,                  /*   USAGE (Rewind) */
    0x75, 0x01,                  /*   REPORT_SIZE (1) */
    0x95, 0x04,                  /*   REPORT_COUNT (4) */
    0x81, 0x22,                  /*   INPUT (Data,Var,Abs,Bit Field) */
    0x15, 0x00,                  /*   LOGICAL_MINIMUM (0) */
    0x25, 0x01,                  /*   LOGICAL_MAXIMUM (1) */
    0x09, 0xe9,                  /*   USAGE (Volume Increment) */
    0x09, 0xea,                  /*   USAGE (Volume Decrement) */
    0x09, 0xe2,                  /*   USAGE (Mute) */
    0x75, 0x01,                  /*   REPORT_SIZE (1) */
    0x95, 0x03,                  /*   REPORT_COUNT (3) */
    0x81, 0x22,                  /*   INPUT (Data,Var,Abs,Bit Field) */
    0x75, 0x05,                  /*   REPORT_SIZE (5) */
    0x95, 0x01,                  /*   REPORT_COUNT (1) */
    0x81, 0x01,                  /*   INPUT (Const,Array,Abs,Bit Field) */
    0xc0,                        /* END_COLLECTION */

    0x06, 0xa0, 0xff,            /* USAGE_PAGE (Vendor-defined 0xFFA0) */
    0x09, 0x01,                  /* USAGE (Vendor-defined 0x0001) */
    0xa1, 0x01,                  /* COLLECTION (Application) */
    0x85, 0x08,                  /*   REPORT_ID (8) */
    0x09, 0x01,                  /*   USAGE (Vendor-defined 0x0001) */
    0x15, 0x00,                  /*   LOGICAL_MINIMUM (0) */
    0x26, 0xff, 0x00,            /*   LOGICAL_MAXIMUM (255) */
    0x75, 0x08,                  /*   REPORT_SIZE (8) */
    0x95, 0x3F,                  /*   REPORT_COUNT (63) */
    0x91, 0x00,                  /*   OUTPUT (Data,Array,Abs,Non-volatile,Bit Field) */
    0x09, 0x02,                  /*   USAGE (Vendor-defined 0x0002) */
    0x75, 0x08,                  /*   REPORT_SIZE (8) */
    0x95, 0x3F,                  /*   REPORT_COUNT (63) */
    0x81, 0x00,                  /*   INPUT (Const,Array,Abs,Bit Field) */
    0xc0
};

/* USB HID class descriptor - Consumer Transport Control Device*/
static const uint8 usb_interface_descriptor_hid_consumer_transport[] =
{
    USB_HID_DESCRIPTOR_LENGTH,                              /* bLength */
    USB_B_DESCRIPTOR_TYPE_HID,                              /* bDescriptorType */
    0x11, 0x01,                                             /* bcdHID */
    0,                                                      /* bCountryCode */
    1,                                                      /* bNumDescriptors */
    USB_B_DESCRIPTOR_TYPE_HID_REPORT,                       /* bDescriptorType */
    LSB(sizeof(usb_report_descriptor_hid_consumer_transport)),      /* wDescriptorLength */
    MSB(sizeof(usb_report_descriptor_hid_consumer_transport))       /* wDescriptorLength */
};

static const EndPointInfo usb_epinfo_hid_consumer_transport[] =
{
    {
        end_point_int_out, /* address */
        end_point_attr_int, /* attributes */
        64, /* max packet size */
        1, /* poll_interval */
        0, /* data to be appended */
        0, /* length of data appended */
    }
};


static const usb_device_class_hid_consumer_transport_config usb_descriptor_hid_consumer_transport =
{
    {usb_interface_descriptor_hid_consumer_transport, sizeof(usb_interface_descriptor_hid_consumer_transport), usb_epinfo_hid_consumer_transport},
    {usb_report_descriptor_hid_consumer_transport, sizeof(usb_report_descriptor_hid_consumer_transport), NULL}
};


/* USB Audio Class Descriptors */
static const uint8 usb_interface_descriptor_control_mic_and_speaker[] =
{
    /* Class Specific Header */
    0x0A,         /* bLength */
    0x24,         /* bDescriptorType = CS_INTERFACE */
    0x01,         /* bDescriptorSubType = HEADER */
    0x00, 0x01,   /* bcdADC = Audio Device Class v1.00 */
#ifdef USB_AUDIO_STEREO_SPEAKER
    0x0A + 0x0c + 0x0a + 0x09 + 0x0c + 0x09 + 0x09, /* wTotalLength LSB */
#else
    0x0A + 0x0c + 0x09 + 0x09 + 0x0c + 0x09 + 0x09, /* wTotalLength LSB */
#endif
    0x00,         /* wTotalLength MSB */
    0x02,         /* bInCollection = 2 AudioStreaming interfaces */
    0x01,         /* baInterfaceNr(1) - AS#1 id */
    0x02,         /* baInterfaceNr(2) - AS#2 id */

    /* Speaker IT */
    0x0c,         /* bLength */
    0x24,         /* bDescriptorType = CS_INTERFACE */
    0x02,         /* bDescriptorSubType = INPUT_TERMINAL */
    USB_AUDIO_SPEAKER_IT,       /* bTerminalID */
    0x01, 0x01,   /* wTerminalType = USB streaming */
    0x00,         /* bAssocTerminal = none */
    USB_AUDIO_CHANNELS_SPEAKER, /* bNrChannels */
    USB_AUDIO_CHANNEL_CONFIG_SPEAKER & 0xFF, USB_AUDIO_CHANNEL_CONFIG_SPEAKER >> 8,   /* wChannelConfig */
    0x00,         /* iChannelName = no string */
    0x00,         /* iTerminal = same as USB product string */

    /* Speaker Features */
#ifdef USB_AUDIO_STEREO_SPEAKER
    0x0a,           /*bLength*/
#else
    0x09,           /*bLength*/
#endif
    0x24,           /*bDescriptorType = CS_INTERFACE */
    0x06,           /*bDescriptorSubType = FEATURE_UNIT*/
    USB_AUDIO_SPEAKER_FU,     /*bUnitId*/
    USB_AUDIO_SPEAKER_IT,     /*bSourceId - Speaker IT*/
    0x01,           /*bControlSize = 1 byte per control*/
    0x03,           /*bmaControls[0] = 03 (Master Channel - mute and volume)*/
    0x00,           /*bmaControls[1] = 00 (Logical Channel 1 - nothing)*/
#ifdef USB_AUDIO_STEREO_SPEAKER
    0x00,           /*bmaControls[2] = 00 (Logical Channel 2 - nothing)*/
#endif
    0x00,           /*iFeature = same as USB product string*/

    /* Speaker OT */
    0x09,         /* bLength */
    0x24,         /* bDescriptorType = CS_INTERFACE */
    0x03,         /* bDescriptorSubType = OUTPUT_TERMINAL */
    USB_AUDIO_SPEAKER_OT,   /* bTerminalID */
    0x01, 0x03,   /* wTerminalType = Speaker */
    0x00,         /* bAssocTerminal = none */
    USB_AUDIO_SPEAKER_FU,   /* bSourceID - Speaker Features */
    0x00,         /* iTerminal = same as USB product string */

    /* Microphone IT */
    0x0c,         /* bLength */
    0x24,         /* bDescriptorType = CS_INTERFACE */
    0x02,         /* bDescriptorSubType = INPUT_TERMINAL */
    USB_AUDIO_MIC_IT,       /* bTerminalID */
    0x01, 0x02,   /* wTerminalType = Microphone */
    0x00,         /* bAssocTerminal = none */
    USB_AUDIO_CHANNELS_MIC, /* bNrChannels */
    USB_AUDIO_CHANNEL_CONFIG_MIC & 0xFF, USB_AUDIO_CHANNEL_CONFIG_MIC >> 8,   /* wChannelConfig */
    0x00,         /* iChannelName = no string */
    0x00,         /* iTerminal = same as USB product string */

    /* Microphone Features */
    0x09,           /*bLength*/
    0x24,           /*bDescriptorType = CS_INTERFACE */
    0x06,           /*bDescriptorSubType = FEATURE_UNIT*/
    USB_AUDIO_MIC_FU,         /*bUnitId*/
    USB_AUDIO_MIC_IT,         /*bSourceId - Microphone IT*/
    0x01,           /*bControlSize = 1 byte per control*/
    0x03,           /*bmaControls[0] = 01 (Master Channel - Mute)*/
    0x00,           /*bmaControls[0] = 00 (Logical Channel 1 - nothing)*/
    0x00,           /*iFeature = same as USB product string*/

    /* Microphone OT */
    0x09,         /* bLength */
    0x24,         /* bDescriptorType = CS_INTERFACE */
    0x03,         /* bDescriptorSubType = OUTPUT_TERMINAL */
    USB_AUDIO_MIC_OT,       /* bTerminalID */
    0x01, 0x01,   /* wTerminalType = USB streaming */
    0x00,         /* bAssocTerminal = none */
    USB_AUDIO_MIC_FU,       /* bSourceID - Microphone Features */
    0x00          /* iTerminal = same as USB product string */
};


static const uint8 usb_interface_descriptor_streaming_mic[] =
{
    /* Class Specific AS interface descriptor */
    0x07,         /* bLength */
    0x24,         /* bDescriptorType = CS_INTERFACE */
    0x01,         /* bDescriptorSubType = AS_GENERAL */
    USB_AUDIO_MIC_OT,       /* bTerminalLink = Microphone OT */
    0x00,         /* bDelay */
    0x01, 0x00,   /* wFormatTag = PCM */

    /* Type 1 format type descriptor */
    0x08 + 0x03,  /* bLength */
    0x24,         /* bDescriptorType = CS_INTERFACE */
    0x02,         /* bDescriptorSubType = FORMAT_TYPE */
    0x01,         /* bFormatType = FORMAT_TYPE_I */
    USB_AUDIO_CHANNELS_MIC, /* bNumberOfChannels */
    0x02,         /* bSubframeSize = 2 bytes */
    0x10,         /* bBitsResolution */
    0x01,         /* bSampleFreqType = 1 discrete sampling freq */
    0xFF & (USB_AUDIO_SAMPLE_RATE_MIC),       /* tSampleFreq */
    0xFF & (USB_AUDIO_SAMPLE_RATE_MIC >> 8),  /* tSampleFreq */
    0xFF & (USB_AUDIO_SAMPLE_RATE_MIC >> 16), /* tSampleFreq */

    /* Class specific AS isochronous audio data endpoint descriptor */
    0x07,         /* bLength */
    0x25,         /* bDescriptorType = CS_ENDPOINT */
    0x01,         /* bDescriptorSubType = AS_GENERAL */
    0x00,         /* bmAttributes = none */
    0x02,         /* bLockDelayUnits = Decoded PCM samples */
    0x00, 0x00     /* wLockDelay */
};

static const uint8 usb_interface_descriptor_streaming_speaker[] =
{
    /* Class Specific AS interface descriptor */
    0x07,         /* bLength */
    0x24,         /* bDescriptorType = CS_INTERFACE */
    0x01,         /* bDescriptorSubType = AS_GENERAL */
    USB_AUDIO_SPEAKER_IT,       /* bTerminalLink = Speaker IT */
    0x00,         /* bDelay */
    0x01, 0x00,   /* wFormatTag = PCM */

    /* Type 1 format type descriptor */
    0x08 + 0x03,/* bLength */
    0x24,         /* bDescriptorType = CS_INTERFACE */
    0x02,         /* bDescriptorSubType = FORMAT_TYPE */
    0x01,         /* bFormatType = FORMAT_TYPE_I */
    USB_AUDIO_CHANNELS_SPEAKER, /* bNumberOfChannels */
    0x02,         /* bSubframeSize = 2 bytes */
    0x10,         /* bBitsResolution */
    0x01,         /* bSampleFreqType = 1 discrete sampling freq */
    0xFF & (USB_AUDIO_SAMPLE_RATE_SPEAKER),       /* tSampleFreq */
    0xFF & (USB_AUDIO_SAMPLE_RATE_SPEAKER >> 8),  /* tSampleFreq */
    0xFF & (USB_AUDIO_SAMPLE_RATE_SPEAKER >> 16), /* tSampleFreq */

    /* Class specific AS isochronous audio data endpoint descriptor */
    0x07,         /* bLength */
    0x25,         /* bDescriptorType = CS_ENDPOINT */
    0x01,         /* bDescriptorSubType = AS_GENERAL */
    0x81,         /* bmAttributes = MaxPacketsOnly and SamplingFrequency control */
    0x02,         /* bLockDelayUnits = Decoded PCM samples */
    0x00, 0x00    /* wLockDelay */
};


static const uint8 usb_audio_endpoint_user_data[] =
{
    0, /* bRefresh */
    0  /* bSyncAddress */
};


/*  Streaming Isochronous Endpoint. Maximum packet size 192 (stereo at 48khz) */
static const EndPointInfo usb_epinfo_streaming_speaker[] =
{
    {
        end_point_iso_in, /* address */
        end_point_attr_iso, /* attributes */
        USB_AUDIO_MAX_PACKET_SIZE_SPEAKER, /* max packet size */
        1, /* poll_interval */
        usb_audio_endpoint_user_data, /* data to be appended */
        sizeof(usb_audio_endpoint_user_data) /* length of data appended */
    }
};


/* Streaming Isochronous Endpoint. Maximum packet size 96 (mono at 48khz) */
static const EndPointInfo usb_epinfo_streaming_mic[] =
{
    {
        end_point_iso_out, /* address */
        end_point_attr_iso, /* attributes */
        USB_AUDIO_MAX_PACKET_SIZE_MIC, /* max packet size */
        1, /* poll_interval */
        usb_audio_endpoint_user_data, /* data to be appended */
        sizeof(usb_audio_endpoint_user_data), /* length of data appended */
    }
};

static const usb_device_class_audio_config usb_descriptor_audio =
{
    {usb_interface_descriptor_control_mic_and_speaker, sizeof(usb_interface_descriptor_control_mic_and_speaker), NULL},
    {usb_interface_descriptor_streaming_mic, sizeof(usb_interface_descriptor_streaming_mic), usb_epinfo_streaming_mic},
    {usb_interface_descriptor_streaming_speaker, sizeof(usb_interface_descriptor_streaming_speaker), usb_epinfo_streaming_speaker}
};


USB_DATA_T USB_RUNDATA;

/***************************************************************************
    Private Functions
****************************************************************************
*/
static uint16   usbHeadsetSpeakerScaledVolumeGet(int16 hostVolume);
/*static int16    UsbHostSpeakerVolumeGet(uint16 deviceVolume);*/
static bool     usbSendVendorCommandAck(void);


/****************************************************************************
NAME
    usb_unhandled_host_command - Unhandled command received from the Host
*/
static void usb_unhandled_host_command(uint8 cmd, uint8 sub_cmd)
{
    USB_DEBUG(("    USB Host Command Unhandled: Cmd[%d] Sub[%d]\n", cmd, sub_cmd));
    UNUSED(cmd);
    UNUSED(sub_cmd);
}



/****************************************************************************
NAME
    usb_process_host_command_connection - Handles the Host command for current Host connection state
*/
static void usb_process_host_command_connection(uint8 data)
{
    if (data)
    {
        /* Host connected */
        usb_set_hid_mode(USB_HID_MODE_HOST);

        usbSendVendorCommandAck();
#if 0
        /* Get the attributes for addr */
        ConnectionSmGetAttributeNow(0,
            &theSource->connection_data.remote_connection_addr,
            sizeof(attributes),
            (uint8*)&attributes);

        /* Send connected device info to host */
        usbSendVendorCommandLastConnectedDeviceInfo(theSource->connection_data.remote_connection_addr,
            attributes.remote_name,
            (uint8)attributes.remote_name_size,
            states_get_state() == SOURCE_STATE_CONNECTED?TRUE:FALSE);
#endif
    }
    else
    {
        /* Host disconnected */
        usb_set_hid_mode(USB_HID_MODE_CONSUMER);
    }
}


/****************************************************************************
NAME
    usb_process_host_command_status - Handles the Host command for inquiry
*/
static void usb_process_host_command_inquiry(uint8 data)
{
    if(data == 1)
    {
        USB_DEBUG(("USB:    --- Enter Inquiry ---\n"));
        /* cancel connecting timer */
        MessageCancelAll(app_get_instance(), APP_CONNECT_REQ);
        /* move to inquiry state */
        states_set_state(SOURCE_STATE_INQUIRING);
        /* indicate this is a forced inquiry, and must remain in this state until a successful connection */
        inquiry_set_forced_inquiry_mode(TRUE);
    }
    else
    {
        if(states_get_state() == SOURCE_STATE_INQUIRING)
        {
            USB_DEBUG(("USB:    --- Exit Inquiry ---\n"));
            /* Finish any ongoing inquiry */
            inquiry_complete();

            inquiry_set_forced_inquiry_mode(FALSE);

            connection_mgr_start_connection_attempt(NULL, PROFILE_AGHFP , 0);
        }
    }
}


/****************************************************************************
NAME
    usbProcessHostCommandDisconnectRemoteDevice - Handles the Host command for disconnecting reomote device
*/

static void usbProcessHostCommandDisconnectRemoteDevice(void)
{
    if(states_get_state() == SOURCE_STATE_CONNECTED)
    {
        states_set_state(SOURCE_STATE_CONNECTABLE);
    }
}

/****************************************************************************
NAME
    usbProcessGetSecurityCommand - Handle get security command
*/
static void usbProcessGetSecurityCommand(void)
{
    //uint16 security;
    uint8 report_bytes[USB_DATA_LINK_REPORT_SIZE];

    /* clear report data */
    memset(report_bytes, 0, USB_DATA_LINK_REPORT_SIZE);
    /*
    security = VmReadSecurity(SECURITY_ENABLE|SECURITY_DEBUG_TRANSPORT|SECURITY_USBDBG_DISABLED|SECURITY_DEBUG_ALLOWED);

    report_bytes[0] = USB_DEVICE_EVENT_SECURITY;
    report_bytes[1] = 2;
    report_bytes[2] = LSB(security);
    report_bytes[3] = MSB(security);
    */

    report_bytes[0] = USB_DEVICE_EVENT_SECURITY;
    report_bytes[1] = 1;
    report_bytes[2] = theSource->encrypted;

    /* send USB Report */
    (usbHidSendInputReport(USB_DATA_LINK_REPORT_ID, USB_DATA_LINK_REPORT_SIZE, report_bytes));

    return;
}

/****************************************************************************
NAME
    usbProcessHostCommandConnectedDevice - Handles the Host command for get connected device
*/
static void usbProcessHostCommandConnectedDevice(void)
{
    ATTRIBUTES_T attributes;
    bdaddr *addr;

    if((states_get_state() == SOURCE_STATE_CONNECTED))
    {
        if(!BdaddrIsZero(&theSource->app_data.primary_addr) && !BdaddrIsZero(&theSource->app_data.secondary_addr))
        {
            addr = &theSource->app_data.secondary_addr;
        }
        else if(!BdaddrIsZero(&theSource->app_data.primary_addr) && BdaddrIsZero(&theSource->app_data.secondary_addr))
        {
            addr = &theSource->app_data.primary_addr;
        }
        else
        {
            addr = connection_mgr_get_remote_address();
        }

        if(BdaddrIsZero(addr))
        {
            return;
        }

        /* Get the attributes for addr and then update the remote name for the addr */
        ConnectionSmGetAttributeNow(0, addr, sizeof(ATTRIBUTES_T), (uint8*)&attributes);

        usbSendVendorCommandConnectedDeviceInfo(*addr, attributes.remote_name, attributes.remote_name_size);
    }
}

/****************************************************************************
NAME
    usb_process_host_command_last_connect_device - Handles the Host command of get last connect device
*/

static void usb_process_host_command_last_connect_device(void)
{
    ATTRIBUTES_T attributes;

    bdaddr *remote_connection_addr;

    memset(&attributes, 0, sizeof(ATTRIBUTES_T));

    remote_connection_addr = connection_mgr_get_remote_address();

    if(!BdaddrIsZero(remote_connection_addr))
    {
        /* Get the attributes for addr */
        ConnectionSmGetAttributeNow(0,
            remote_connection_addr,
            sizeof(attributes),
            (uint8*)&attributes);
    }

    /* Send connected device info to host */
    usbSendVendorCommandLastConnectedDeviceInfo(*remote_connection_addr,
        attributes.remote_name,
        (uint8)attributes.remote_name_size,
        states_get_state() == SOURCE_STATE_CONNECTED?TRUE:FALSE);
}

/****************************************************************************
NAME
    usbProcessHostCommandGetSn - Handles the Host command for get device serial number
*/
static void usbProcessHostCommandGetSn(void)
{
#if 0
    uint16 sn[8];
    uint16 sn_size;

    sn_size = ps_read_serial_number(sn);

    usbSendVendorCommandSerialNumber(sn, sn_size);
#endif
}


/****************************************************************************
NAME
    usbProcessHostCommandGetLinkey - Handles the Host command for get linkey of last connect device
*/
static void usbProcessHostCommandGetLinkKey(void)
{
    if(!BdaddrIsZero(connection_mgr_get_remote_address()))
    {
        ConnectionSmGetAuthDevice(&theSource->connectionTask, connection_mgr_get_remote_address());
    }
}



/****************************************************************************
NAME
    usbProcessHostCommandGetLocalAddr - Handles the Host command for get local bd_addr
*/
static void usbProcessHostCommandGetLocalAddr(void)
{
    bdaddr local_addr;

    connection_mgr_get_local_address(&local_addr);

    usbSendVendorCommandLocalAddr(local_addr);
}


/****************************************************************************
NAME
    usbProcessHostCommandGetCodec - Handles the Host command for get codec
*/
static void usbProcessHostCommandAudioCodec(void)
{
    uint8 report_bytes[USB_DATA_LINK_REPORT_SIZE];
    a2dpInstance *inst = a2dp_get_instance_from_device_id(0);

    if(inst)
    {
        /* clear report data */
        memset(report_bytes, 0, USB_DATA_LINK_REPORT_SIZE);

        report_bytes[0] = USB_HOST_COMMAND_AUDIO_CODEC;
        report_bytes[1] = 2;
        report_bytes[2] = inst->seid;
        report_bytes[3] = 0; /*Quality*/
        /* send USB Report */
        (usbHidSendInputReport(USB_DATA_LINK_REPORT_ID, USB_DATA_LINK_REPORT_SIZE, report_bytes));
    }

    return;
}

/****************************************************************************
NAME
    usbProcessHostCommandReadDeviceSettings - Handles the Host command of read device setting
*/
static void usbProcessHostCommandReadDeviceSettings(void)
{
    uint8 report_bytes[USB_DATA_LINK_REPORT_SIZE];

    /* clear report data */
    memset(report_bytes, 0, USB_DATA_LINK_REPORT_SIZE);

    report_bytes[0] = USB_HOST_COMMAND_DEVICE_SETTING;

    sourcePsReadDeviceSettings(report_bytes + 1);

    /* send USB Report */
    (usbHidSendInputReport(USB_DATA_LINK_REPORT_ID, USB_DATA_LINK_REPORT_SIZE, report_bytes));
}

/****************************************************************************
NAME
    usbProcessHostCommandWritePairedDevice - Handles the Host command of writing device to TDL
*/
static void usbProcessHostCommandWritePairedDevice(uint8 *pData)
{
    UNUSED(pData);
#if 0
    bdaddr addr;

    addr.nap = (data[0] << 8 | data[1]) & 0xFFFF;
    addr.uap = data[2] & 0xFF;
    addr.lap = ((uint32)data[3] << 16 | (uint32)data[4] << 8 | (uint32)data[5]) & 0xFFFFFF;

    uint16 link_key[8];
    EFDB8ADC12B9A283D37C4D9921ACA833
    link_key[7] = 0xDBEF;
    link_key[6] = 0xDC8A;
    link_key[5] = 0xB912;
    link_key[4] = 0x83A2;
    link_key[3] = 0x7CD3;
    link_key[2] = 0x994D;
    link_key[1] = 0xAC21;
    link_key[0] = 0x33A8;

    bdaddr bd_addr;
    uint16 link_key[8];

    bd_addr.nap = 0x70BF;
    bd_addr.uap = 0x92;
    bd_addr.lap = 0xD52048;

    link_key[7] = 0xDBEF;
    link_key[6] = 0xDC8A;
    link_key[5] = 0xB912;
    link_key[4] = 0x83A2;
    link_key[3] = 0x7CD3;
    link_key[2] = 0x994D;
    link_key[1] = 0xAC21;
    link_key[0] = 0x33A8;

    connection_add_paired_device(&bd_addr, (const uint8 *)link_key);
    connection_mgr_set_remote_device_address(&bd_addr);
    //connection_mgr_start_connection_attempt(&bd_addr, PROFILE_AGHFP, 0);

#endif
}

/****************************************************************************
NAME
    usbProcessHostCommandConnectRemoteDevice - Handles the Host command for connecting remote device
*/

static void usbProcessHostCommandConnectRemoteDevice(uint8 *data)
{
    bdaddr addr;

    USB_DEBUG(("USB: Connect to"));

    if(a2dp_allow_more_connections())
    {
        addr.nap = (data[0] << 8 | data[1]) & 0xFFFF;
        addr.uap = data[2] & 0xFF;
        addr.lap = ((uint32)data[3] << 16 | (uint32)data[4] << 8 | (uint32)data[5]) & 0xFFFFFF;

       if(states_get_state() == SOURCE_STATE_CONNECTABLE)
        {
            MessageSend(app_get_instance(), APP_UI_CONNECTING_STATE, 0);
        }

        /* initialise the connection with the connection manager */
        connection_mgr_start_connection_attempt(&addr, PROFILE_AGHFP, 0);
    }
}


/****************************************************************************
NAME
    usbHidHandleVendorReport - Handle host report
*/
static void usbHidHandleVendorReport(const uint16 size_data, const uint8 *data)
{
    if (size_data >= 2)
    {
        /*
         *  Byte 0 - Report ID
         */
        switch (data[1])
        {
            case USB_HOST_COMMAND_HOST_CONNECTION:
            {
                usb_process_host_command_connection(data[2]);
            }
            break;

            case USB_HOST_COMMAND_INQUIRY:
            {
                if (usb_get_hid_mode() == USB_HID_MODE_HOST)
                {
                    usb_process_host_command_inquiry(data[2]);
                }
            }
            break;

            case USB_HOST_COMMAND_CONNECT_REMOTE_DEVICE:
            {
                if (usb_get_hid_mode() == USB_HID_MODE_HOST)
                {
                    usbProcessHostCommandConnectRemoteDevice((uint8 *)(data + 2));
                }
            }
            break;

            case USB_HOST_COMMAND_DISCONNECT_REMOTE_DEVICE:
            {
                if (usb_get_hid_mode() == USB_HID_MODE_HOST)
                {
                    usbProcessHostCommandDisconnectRemoteDevice();
                }
            }
            break;

            case USB_HOST_COMMAND_GET_LAST_CONNECT_DEVICE:
            {
                if (usb_get_hid_mode() == USB_HID_MODE_HOST)
                {
                    usb_process_host_command_last_connect_device();
                }
            }
            break;

            case USB_HOST_COMMAND_GET_SECURITY:
            {
                if (usb_get_hid_mode() == USB_HID_MODE_HOST)
                {
                    usbProcessGetSecurityCommand();
                }
            }
            break;

            case USB_HOST_COMMAND_CONNECTED_DEVICE:
            {
                if (usb_get_hid_mode() == USB_HID_MODE_HOST)
                {
                    usbProcessHostCommandConnectedDevice();
                }
            }
            break;

            case USB_HOST_COMMAND_ENTER_TEST_MODE:
            {
                if (usb_get_hid_mode() == USB_HID_MODE_HOST)
                {
                    MessageCancelAll(app_get_instance(), APP_CONNECT_REQ);
                    states_set_state(SOURCE_STATE_TEST_MODE);
                }
            }
            break;

            case USB_HOST_COMMAND_GET_SERIAL_NUMBER:
            {
                if (usb_get_hid_mode() == USB_HID_MODE_HOST)
                {
                    usbProcessHostCommandGetSn();
                }
            }
            break;

            case USB_HOST_COMMAND_GET_LINK_KEY:
            {
                if (usb_get_hid_mode() == USB_HID_MODE_HOST)
                {
                    usbProcessHostCommandGetLinkKey();
                }
            }
            break;

            case USB_HOST_COMMAND_GET_LOCAL_ADDRESS:
            {
                if (usb_get_hid_mode() == USB_HID_MODE_HOST)
                {
                    usbProcessHostCommandGetLocalAddr();
                }
            }
            break;

            case USB_HOST_COMMAND_AUDIO_CODEC:
            {
                if (usb_get_hid_mode() == USB_HID_MODE_HOST)
                {
                    usbProcessHostCommandAudioCodec();
                }
            }
            break;

            case USB_HOST_COMMAND_WRITE_NEW_PAIRED_DEVICE:
            {
                if (usb_get_hid_mode() == USB_HID_MODE_HOST)
                {
                    usbProcessHostCommandWritePairedDevice((uint8 *)(data + 2));
                }
            }
            break;

            case USB_HOST_COMMAND_FW_UPGRADE:
            {
                sourceUpgradeHandleHidMessage((uint8 *)(data + 2), size_data - 2);
            }
            break;

            case USB_HOST_COMMAND_DEVICE_SETTING:
            {
                if (usb_get_hid_mode() == USB_HID_MODE_HOST)
                {
                    if(data[2] == 0)
                    {
                        /*Read*/
                        usbProcessHostCommandReadDeviceSettings();
                    }
                    else
                    {
                        /*Write*/
                        sourcePsHandleDeviceSetting((uint8 *)(data + 3));
                    }
                }
            }
            break;
			
            default:
            {
                USB_DEBUG(("USB: Host Command not recognised %d\n", data[1]));
                usb_unhandled_host_command(data[1], data[2]);
            }
            break;
        }
    }
}



static void usbHidHandleOutputTelephonyReport(const uint16 size_data, const uint8 *data)
{
    UNUSED(size_data);
    UNUSED(data);

    uint16 report, diff;

    report = ((data[2] << 8) | data[1]);
    //USB_DEBUG(("USB: HID Telephony Report %d %d\n", data[1], data[2]));

    if(USB_RUNDATA.outputTelephonyReport != report)
    {
        diff = report ^ USB_RUNDATA.outputTelephonyReport;

        if(diff & USB_HID_TELEPHONY_REPORT_OFF_HOOK_MASK)
        {
            if(report & USB_HID_TELEPHONY_REPORT_OFF_HOOK_MASK)
            {
                call_control_set_state(SOFTPHONE, CALL_STATE_OFF_HOOK, TRUE);
            }
            else
            {
                call_control_set_state(SOFTPHONE, CALL_STATE_OFF_HOOK, FALSE);
            }
        }

        if(diff & USB_HID_TELEPHONY_REPORT_MUTE_MASK)
        {
            if(report & USB_HID_TELEPHONY_REPORT_MUTE_MASK)
            {
                call_control_set_state(SOFTPHONE, CALL_STATE_MUTE, TRUE);
            }
            else
            {
                call_control_set_state(SOFTPHONE, CALL_STATE_MUTE, FALSE);
            }
        }

        if(diff & USB_HID_TELEPHONY_REPORT_RING_MASK)
        {
            if(report & USB_HID_TELEPHONY_REPORT_RING_MASK)
            {
                call_control_set_state(SOFTPHONE, CALL_STATE_RING, TRUE);
            }
            else
            {
                call_control_set_state(SOFTPHONE, CALL_STATE_RING, FALSE);
            }
        }

        if(diff & USB_HID_TELEPHONY_REPORT_HOLD_MASK)
        {
            if(report & USB_HID_TELEPHONY_REPORT_HOLD_MASK)
            {
                call_control_set_state(SOFTPHONE, CALL_STATE_RING, TRUE);
            }
            else
            {
                call_control_set_state(SOFTPHONE, CALL_STATE_RING, FALSE);
            }
        }
    }

    USB_RUNDATA.outputTelephonyReport = report;
}



/** Handle meaasges to the speaker volume terminal.
  * @param[in,out] usbresp Struct containing the original request and the response
  * @param[in] req Source which contains the incoming data
  * @param[in] resp Sink Sink where the resulting data should go (reply)
  */
static void usbHandleSpeakerVolume(UsbResponse* usbresp, Source req, Sink resp)
{
  uint16 volume, mute;
  uint8 *ptr;

  switch(REQ_CS(usbresp->original_request))
  {
    case VOLUME_CONTROL:

      /* Check request type */
      switch(usbresp->original_request.bRequest)
      {
        case GET_MIN:

          if (USB_RUNDATA.hostOperatingSystem == HOST_OS_UNKNOWN)
          {
            USB_DEBUG(("USB: HOST is windows xp\n"));
            /* First request from windows xp is get min */
            USB_RUNDATA.hostOperatingSystem = HOST_OS_PS4;
          }
          ptr = SinkMap(resp) + SinkClaim(resp, 2);
          ptr[0] = LSB(SPEAKER_VOLUME_MIN);
          ptr[1] = MSB(SPEAKER_VOLUME_MIN);
          usbresp->data_length = 0x02;
          usbresp->success = TRUE;
          break;

        case GET_MAX:
          if (USB_RUNDATA.hostOperatingSystem == HOST_OS_UNKNOWN)
          {
            USB_DEBUG(("USB: HOS is Linux\n"));
            /* First request from Linux is get max */
            USB_RUNDATA.hostOperatingSystem = HOST_OS_LINUX;
          }
          ptr = SinkMap(resp) + SinkClaim(resp, 2);
          ptr[0] = LSB(SPEAKER_VOLUME_MAX);
          ptr[1] = MSB(SPEAKER_VOLUME_MAX);
          usbresp->data_length = 0x02;
          usbresp->success = TRUE;
          break;

        case GET_RES:
          ptr = SinkMap(resp) + SinkClaim(resp, 2);
          ptr[0] = LSB(SPEAKER_VOLUME_RES);
          ptr[1] = MSB(SPEAKER_VOLUME_RES);
          usbresp->data_length = 0x02;
          usbresp->success = TRUE;
          break;

        case SET_RES:
          USB_DEBUG(("USB: USB_AUDIO_REQ_SET_RES\n"));
          /* not supported */
          usbresp->success = FALSE;
          break;

        case GET_CUR:
          ptr = SinkMap(resp) + SinkClaim(resp, 2);
          if(USB_RUNDATA.hostOperatingSystem == HOST_OS_UNKNOWN)
          {
            USB_DEBUG(("USB: HOS is windows 7\n"));
            /* First request from windows 7 is get current */
            USB_RUNDATA.hostOperatingSystem = HOST_OS_WINDOWS_7;
          }
          else if(USB_RUNDATA.hostOperatingSystem == HOST_OS_PS4)
          {
              USB_DEBUG(("USB: HOST is windows xp\n"));
              USB_RUNDATA.hostOperatingSystem = HOST_OS_WINDOWS_XP;
          }

          ptr[0] = LSB(USB_RUNDATA.hostSpeakerVolume);
          ptr[1] = MSB(USB_RUNDATA.hostSpeakerVolume);
          usbresp->data_length = 0x02;
          usbresp->success = TRUE;
          break;

        case SET_CUR:
          USB_RUNDATA.pcVolumeWait = FALSE;
		  /* Don't change volume if OS is Sony Play Station */
          if(USB_RUNDATA.hostOperatingSystem != HOST_OS_PS4)
          {
              USB_RUNDATA.hostSpeakerVolume = (int16)(((uint16)(SourceMap(req)[1])<<8) + (uint16)(SourceMap(req)[0]));

              /* 0x8000 is silent according the the standard */
              if(USB_RUNDATA.hostSpeakerVolume == (int16)0x8000)
              {
                volume = 0;
              }
              else
              {
                volume = usbHeadsetSpeakerScaledVolumeGet(USB_RUNDATA.hostSpeakerVolume);
              }

              switch(USB_RUNDATA.volumeSyncState)
              {
                case USB_HID_VOLUME_GOING_UP:
                  if(volume >= USB_RUNDATA.headsetSpeakerVolume)
                  {
                    USB_RUNDATA.volumeSyncState = USB_HID_VOLUME_SYNCED;
                  }
                  else
                  {
                    /* Continuely change volume until reach target level */
                    MessageSend(&USB_RUNDATA.usbTask, USB_CLASS_MSG_VOLUME_GOING_UP, NULL);
                    usbHidSendMediaCommand(USB_HID_MEDIA_VOL_UP);
                  }
                  break;
                case USB_HID_VOLUME_GOING_DOWN:
                  if(volume <= USB_RUNDATA.headsetSpeakerVolume)
                  {
                    USB_RUNDATA.volumeSyncState = USB_HID_VOLUME_SYNCED;
                  }
                  else
                  {
                    /* Continuely change volume until meet target level */
                    MessageSend(&USB_RUNDATA.usbTask, USB_CLASS_MSG_VOLUME_GOING_DOWN, NULL);
                    usbHidSendMediaCommand(USB_HID_MEDIA_VOL_DOWN);
                  }
                  break;
                case USB_HID_VOLUME_SYNCED:
                    USB_DEBUG(("USB: Audio Route to %d\n", audio_get_audio_routed()));

                    if((states_get_state() == SOURCE_STATE_CONNECTED) && (USB_RUNDATA.headsetSpeakerVolume != volume))
                    {
                        switch(audio_get_audio_routed())
                        {
                            case AUDIO_ROUTED_NONE:
                            case AUDIO_ROUTED_A2DP:
                                avrcp_send_source_volume(NULL, volume, FALSE);
                                break;
                            case AUDIO_ROUTED_AGHFP:
                                aghfp_set_speaker_volume(NULL, volume);
                                break;
                        }
                    }

                    USB_RUNDATA.headsetSpeakerVolume = volume;

                    volume_set_speaker_volume(volume, VOL_TYPE_USB);
                    break;
                }
          }

          usbresp->success = TRUE;
          break;

        default:
          /* Bad request */
          USB_DEBUG(("USB: Bad volume request!\n"));
          break;
      }
      break;
    case MUTE_CONTROL:
      USB_DEBUG(("USB: USB_AUDIO_MUTE_CONTROL\n"));
      /* Check request type */
      switch(usbresp->original_request.bRequest)
      {
        case SET_CUR:
          mute = (uint16)SourceMap(req)[0];
          USB_RUNDATA.hostSpeakerMute = mute;

          if(USB_RUNDATA.hostSpeakerMute)
          {
              MessageSend(app_get_instance(), APP_SPEAKER_MUTE, 0);
          }
          else
          {
              MessageSend(app_get_instance(), APP_SPEAKER_UNMUTE, 0);
          }

          usbresp->success = TRUE;
          break;

        case GET_CUR:
          ptr = SinkMap(resp) + SinkClaim(resp, 1);
          /* USB speaker is muted ? */
          ptr[0] = USB_RUNDATA.hostSpeakerMute ? 1 : 0;
          usbresp->data_length = 0x01;
          usbresp->success = TRUE;
          break;

        default: /* MUTE bad request */
          /* Bad request */
          USB_DEBUG(("USB: Bad speaker mute request!\n"));
          break;
      }
      break;
    default:
      /* Bad request */
      USB_DEBUG(("USB: Bad control request!\n"));
      break;
  }
}

/*
 * Handle meaasges to the microphone volume terminal.
 */
static void usbHandleMicrophoneVolume(UsbResponse* usbresp, Source req, Sink resp)
{
  int16 volume;
  uint8 *ptr;
  const uint8 *data;

  switch(REQ_CS(usbresp->original_request))
  {
    case VOLUME_CONTROL:
      USB_DEBUG(("USB: USB_AUDIO_VOLUME_CONTROL\n"));

      /* Check Volume request type */
      switch(usbresp->original_request.bRequest)
      {
        case GET_MIN:
          ptr = SinkMap(resp) + SinkClaim(resp, 2);
          ptr[0] = LSB(MIC_VOLUME_MIN);
          ptr[1] = MSB(MIC_VOLUME_MIN);
          usbresp->data_length = 0x02;
          usbresp->success = TRUE;
          break;

        case GET_MAX:
          ptr = SinkMap(resp) + SinkClaim(resp, 2);
          ptr[0] = LSB(MIC_VOLUME_MAX);
          ptr[1] = MSB(MIC_VOLUME_MAX);
          usbresp->data_length = 0x02;
          usbresp->success = TRUE;
          break;

        case GET_RES:
          ptr = SinkMap(resp) + SinkClaim(resp, 2);
          ptr[0] = LSB(MIC_VOLUME_RES);
          ptr[1] = MSB(MIC_VOLUME_RES);
          usbresp->data_length = 0x02;
          usbresp->success = TRUE;
          break;

        case GET_CUR:
          ptr = SinkMap(resp) + SinkClaim(resp, 2);

          volume = USB_RUNDATA.hostMicVolume;

          ptr[0] = LSB(volume);
          ptr[1] = MSB(volume);
          usbresp->data_length = 0x02;
          usbresp->success = TRUE;
          break;

        case SET_CUR:
          volume = ((uint16)(SourceMap(req)[1])<<8) + (uint16)(SourceMap(req)[0]);

          USB_RUNDATA.hostMicVolume = volume;

          usbresp->success = TRUE;
          break;

        case SET_RES:
          usbresp->success = FALSE;
          break;

        default: /*Bad mic gain request */
        /* Bad request */
        USB_DEBUG(("USB: Bad mic gain request!\n"));
        break;
      }
      break;

    case MUTE_CONTROL:
      USB_DEBUG(("USB: USB_AUDIO_MUTE_CONTROL\n"));
      /* Check request type */
      switch(usbresp->original_request.bRequest)
      {
        case SET_CUR:
          data = SourceMap(req);
          USB_RUNDATA.hostMicMute = data[0];
          usbresp->success = TRUE;
          break;

        case GET_CUR:
          ptr = SinkMap(resp) + SinkClaim(resp, 1);
          ptr[0] = USB_RUNDATA.hostMicMute?1:0;

          usbresp->data_length = 0x01;
          usbresp->success = TRUE;
          break;

        default: /* MUTE bad request */
          /* Bad request */
          USB_DEBUG(("USB: Bad mute request!\n"));
          break;
      }
      break;
    default: /* bad control request */
      /* Bad request */
      USB_DEBUG(("USB: Bad control request!\n"));
      break;
  }
}


static void handleAudioControlClassRequest(Source req)
{
    uint16 packet_size;
    Sink resp = StreamSinkFromSource(req);
    uint16 entity;

    /* Check for outstanding Class requests */
    while((packet_size = SourceBoundary(req)) != 0)
    {
        /* Build the response. It must contain the original request,
           so copy from the source header */
        UsbResponse usbresp;
        memcpy(&usbresp.original_request, SourceMapHeader(req), sizeof(UsbRequest));
        usbresp.data_length = 0;
        usbresp.success = FALSE;

        /* Check request type */
        if((usbresp.original_request.bmRequestType & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_CLASS)
        {
            /* Get terminal ID */
            entity = (usbresp.original_request.wIndex >> 0x08);

            switch(entity)
            {
              case USB_AUDIO_SPEAKER_FU:
                usbHandleSpeakerVolume(&usbresp, req, resp);
                break;
              case USB_AUDIO_MIC_FU:
                usbHandleMicrophoneVolume(&usbresp, req, resp);
                break;
              default:
                break;
            }
        }

        /* Send response */
        if(usbresp.data_length)
        {
          SinkFlushHeader(resp, usbresp.data_length, (void*)&usbresp, sizeof(UsbResponse));
        }
        else
        {
          /* Sink packets can never be zero-length, so flush a dummy byte */
          SinkClaim(resp, 1);
          SinkFlushHeader(resp, 1, (void*)&usbresp, sizeof(UsbResponse));
        }

        /* Discard the original request */
        SourceDrop(req, packet_size);
    }
}

static void handleAudioStreamMicClassRequest(Source req)
{
    uint16 packet_size, newRate;
    uint8 *ptr;
    Sink resp = StreamSinkFromSource(req);

    /* Check for outstanding Class requests */
    while ((packet_size = SourceBoundary(req)) != 0)
    {
      /* Build the response. It must contain the original request,
      so copy from the source header.*/
      UsbResponse usbresp;
      memcpy(&usbresp.original_request, SourceMapHeader(req), sizeof(UsbRequest));

      /* Set the response fields to default values to make the code below simpler */
      usbresp.success = TRUE;
      usbresp.data_length = 0;

      /* Endpoint only allows SET_/GET_ of sampling frequency */
      if(((usbresp.original_request.wValue >> 8) == SAMPLING_FREQ_CONTROL) && (usbresp.original_request.wLength == 3))
      {
        if(usbresp.original_request.bRequest == SET_CUR)
        {
            newRate = (uint16)SourceMap(req)[0] | ((uint16)SourceMap(req)[1] << 8);
            if(newRate != USB_RUNDATA.hostMicSampleRate)
            {
                USB_RUNDATA.hostMicSampleRate = newRate;
            }
            USB_DEBUG(("USB: Set Mic Rate %u\n", USB_RUNDATA.hostMicSampleRate));
        }
        else if (usbresp.original_request.bRequest == GET_CUR)
        {
            /* Return current value */
            ptr = SinkMap(resp) + SinkClaim(resp, 3);
            ptr[0] = (uint8)(USB_RUNDATA.hostMicSampleRate & 0xff);
            ptr[1] = (uint8)(USB_RUNDATA.hostMicSampleRate >> 8);
            ptr[2] = (((uint32)USB_RUNDATA.hostMicSampleRate) >> 16);
            usbresp.data_length = 3;
            USB_DEBUG(("USB: Mic Rate %u\n", USB_RUNDATA.hostMicSampleRate));
        }
        else
        {
            usbresp.success = FALSE;
        }
      }
      else
      {
        usbresp.success = FALSE;
      }

      /* Send response */
      if(usbresp.data_length)
      {
        SinkFlushHeader(resp, usbresp.data_length, (void *)&usbresp, sizeof(UsbResponse));
      }
      else
      {
        /* Sink packets can never be zero-length, so flush a dummy byte */
        SinkClaim(resp, 1);
        SinkFlushHeader(resp, 1, (void *)&usbresp, sizeof(UsbResponse));
      }

      /* Discard the original request */
      SourceDrop(req, packet_size);
    }

}

static void handleAudioStreamSpeakerClassRequest(Source req)
{
    uint16 packet_size, newRate;
    uint8 *ptr;
    Sink resp = StreamSinkFromSource(req);

    /* Check for outstanding Class requests */
    while ((packet_size = SourceBoundary(req)) != 0)
    {
      /* Build the response. It must contain the original request,
      so copy from the source header.*/
      UsbResponse usbresp;
      memcpy(&usbresp.original_request, SourceMapHeader(req), sizeof(UsbRequest));

      /* Set the response fields to default values to make the code below simpler */
      usbresp.success = TRUE;
      usbresp.data_length = 0;

      /* Endpoint only allows SET_/GET_ of sampling frequency */
      if(((usbresp.original_request.wValue >> 8) == SAMPLING_FREQ_CONTROL) && (usbresp.original_request.wLength == 3))
      {
        if(usbresp.original_request.bRequest == SET_CUR)
        {
            newRate = (uint16)SourceMap(req)[0] | ((uint16)SourceMap(req)[1] << 8);
            if(newRate != USB_RUNDATA.hostSpeakerSampleRate)
            {
                USB_RUNDATA.hostSpeakerSampleRate = newRate;
            }
            USB_DEBUG(("USB: Set Speaker Rate %u\n", USB_RUNDATA.hostSpeakerSampleRate));
        }
        else if (usbresp.original_request.bRequest == GET_CUR)
        {
            /* Return current value */
            ptr = SinkMap(resp) + SinkClaim(resp, 3);
            ptr[0] = (uint8)(USB_RUNDATA.hostSpeakerSampleRate & 0xff);
            ptr[1] = (uint8)(USB_RUNDATA.hostSpeakerSampleRate >> 8);
            ptr[2] = (((uint32)USB_RUNDATA.hostSpeakerSampleRate) >> 16);
            usbresp.data_length = 3;
            USB_DEBUG(("USB: Speaker Rate %u\n", USB_RUNDATA.hostSpeakerSampleRate));
        }
        else
        {
            usbresp.success = FALSE;
        }
      }
      else
      {
        usbresp.success = FALSE;
      }

      /* Send response */
      if(usbresp.data_length)
      {
        SinkFlushHeader(resp, usbresp.data_length, (void *)&usbresp, sizeof(UsbResponse));
      }
      else
      {
        /* Sink packets can never be zero-length, so flush a dummy byte */
        SinkClaim(resp, 1);
        SinkFlushHeader(resp, 1, (void *)&usbresp, sizeof(UsbResponse));
      }

      /* Discard the original request */
      SourceDrop(req, packet_size);
    }
}

static void handleHidClassRequest(Source source)
{
    static uint8 idle_rate = 0;
    Sink sink;
    uint16 packet_size;
    bool early_flushed;

    while ((packet_size = SourceBoundary(source)) != 0)
    {
        UsbResponse resp;
        early_flushed = FALSE;
        /* Build the response. It must contain the original request, so copy
           from the source header. */
        memcpy(&resp.original_request, SourceMapHeader(source), sizeof(UsbRequest));

        /* Set the response fields to default values to make the code below simpler */
        resp.success = FALSE;
        resp.data_length = 0;

        switch (resp.original_request.bRequest)
        {
            /* GET_REPORT */
            case 0x01:
            {
                USB_DEBUG(("USB: HID GET_REPORT\n"));
                break;
            }

            /* GET_IDLE */
            case 0x02:
            {
                USB_DEBUG(("USB: HID GET_IDLE\n"));
                break;
            }

            /* SET_REPORT */
            case 0x09:
            {
                const uint8 *in = SourceMap(source);
                uint16 size_data = resp.original_request.wLength;
                uint8 report_id = resp.original_request.wValue & 0xff;
                USB_DEBUG(("USB: HID Set_Report, report_id = %d\n", report_id));

                resp.success = TRUE;

                if (size_data)
                {
                    switch(report_id & 0xf)
                    {
                    case USB_DATA_LINK_REPORT_ID:
                        /* this is a Vendor SET_REPORT received from the Host */
                        usbHidHandleVendorReport(size_data, in);
                        break;
                    case USB_TELEPHONY_REPORT_ID:
                        /* this is a Telephony SET_REPORT received from the Host */
                        usbHidHandleOutputTelephonyReport(size_data, in);
                        break;
                    default:
                        break;
                    }
                }

                break;
            }

            /* SET_IDLE */
            case 0x0A:
            {
                USB_DEBUG(("USB: HID Set_Idle\n"));
                idle_rate = (resp.original_request.wValue >> 8) & 0xff;
				USB_RUNDATA.notMacOs = TRUE;
                resp.success = TRUE;
                break;
            }

            default:
            {
                break;
            }
        }

        /* Send response */
        if (!early_flushed)
        {
            sink = StreamSinkFromSource(source);

            if (resp.data_length)
            {
                (void)SinkFlushHeader(sink, resp.data_length, (uint16 *)&resp, sizeof(UsbResponse));
            }
            else
            {
                /* Sink packets can never be zero-length, so flush a dummy byte */
                (void) SinkClaim(sink, 1);
                (void) SinkFlushHeader(sink, 1, (uint16 *) &resp, sizeof(UsbResponse));
            }
        }
        /* Discard the original request */
        SourceDrop(source, packet_size);
    }
}


/** Helper function for allocating space in Sink */
static uint8 *claimSink(Sink sink, uint16 size)
{
    uint8 *dest = SinkMap(sink);
    uint16 claim_result = SinkClaim(sink, size);
    if (claim_result == 0xffff)
    {
        return NULL;
    }
    return (dest + claim_result);
}


bool usbHidSendChunkData(uint8 *report, uint16 size_report)
{
    if(USB_RUNDATA.state == USB_STATE_ENUMERATED || USB_RUNDATA.state == USB_STATE_ATTACHED)
    {
        Sink sink = StreamUsbEndPointSink(usb_descriptor_hid_consumer_transport.interface.end_point_info->bEndpointAddress);
        uint8 *pSource = NULL;

        pSource = claimSink(sink, (uint16)(size_report));

        if (pSource)
        {
            /* report ID is first byte */
            memmove(pSource, report, size_report);

            return SinkFlush(sink, (uint16)size_report);
        }
    }

    return FALSE;
}


bool usbHidSendInputReport(uint16 report_id, uint16 size_report, uint8 *report)
{
    if(USB_RUNDATA.state == USB_STATE_ENUMERATED || USB_RUNDATA.state == USB_STATE_ATTACHED)
    {
        Sink sink = StreamUsbEndPointSink(usb_descriptor_hid_consumer_transport.interface.end_point_info->bEndpointAddress);
        uint8 *data = NULL;

        data = claimSink(sink, (uint16)(size_report + 1));

        if (data)
        {
            USB_DEBUG(("USB: Send input report, ID = %d\n data_0 = [0x%x] data_1 = [0x%x]\n", report_id, report[0], report[1]));

            /* report ID is first byte */
            data[0] = report_id;

            memcpy(data + 1, report, sizeof(uint8) * size_report);

            return SinkFlush(sink, (uint16)(size_report + 1));
        }
    }

    return FALSE;
}


/***************************************************************************
Functions
****************************************************************************
*/

/****************************************************************************
NAME
    usb_msg_handler - Handles USB messages
*/
static void usb_msg_handler(Task task, MessageId id, Message message)
{
    UNUSED(task);
    switch (id)
    {
        case MESSAGE_MORE_DATA:
        {
            USB_DEBUG(("USB: MESSAGE_MORE_DATA\n"));

            /* Check if we are receiving USB control data */
            if(StreamUsbClassSource(USB_RUNDATA.usb_interface[usb_interface_hid_consumer_transport]) == ((MessageMoreData*)message)->source)
            {
                handleHidClassRequest(StreamUsbClassSource(USB_RUNDATA.usb_interface[usb_interface_hid_consumer_transport]));
            }
            else if(StreamUsbClassSource(USB_RUNDATA.usb_interface[usb_interface_audio_control]) == ((MessageMoreData*)message)->source)
            {
                handleAudioControlClassRequest(StreamUsbClassSource(USB_RUNDATA.usb_interface[usb_interface_audio_control]));
            }
            else if(StreamUsbClassSource(USB_RUNDATA.usb_interface[usb_interface_audio_streaming_mic]) == ((MessageMoreData*)message)->source)
            {
                handleAudioStreamMicClassRequest(StreamUsbClassSource(USB_RUNDATA.usb_interface[usb_interface_audio_streaming_mic]));
            }
            else if(StreamUsbClassSource(USB_RUNDATA.usb_interface[usb_interface_audio_streaming_speaker]) == ((MessageMoreData*)message)->source)
            {
                handleAudioStreamSpeakerClassRequest(StreamUsbClassSource(USB_RUNDATA.usb_interface[usb_interface_audio_streaming_speaker]));
            }
        }
        break;

        /* message indicating that USB has enumerated */
        case MESSAGE_USB_ENUMERATED:
        {
            USB_DEBUG(("USB: MESSAGE_USB_ENUMERATED\n"));
            USB_RUNDATA.state = USB_STATE_ENUMERATED;
            MessageCancelAll(app_get_instance(), APP_UPDATE_AUDIO_ROUTING);
            MessageSendLater(app_get_instance(), APP_UPDATE_AUDIO_ROUTING, 0, 500);
        }
        break;

        case MESSAGE_USB_DECONFIGURED:
        {
            USB_DEBUG(("USB: MESSAGE_USB_DECONFIGURED\n"));
        }
        break;

        /* message that indicates an alternative setting is applied on a USB interface */
        case MESSAGE_USB_ALT_INTERFACE:
        {
            MessageUsbAltInterface* ind = (MessageUsbAltInterface*)message;
            USB_DEBUG(("USB: MESSAGE_USB_ALT_INTERFACE %d %d\n", ind->interface, ind->altsetting));

            if(usb_interface_audio_streaming_mic == ind->interface)
            {
                USB_RUNDATA.micAltsetting = ind->altsetting;
                
                if (ind->altsetting)
                {
                    MessageCancelAll(app_get_instance(), APP_MIC_AUDIO_INACTIVE);
                    MessageSendLater(app_get_instance(), APP_MIC_AUDIO_ACTIVE, 0, MIC_AUDIO_ACTIVE_DELAY);
                }
                else
                {
                    MessageCancelAll(app_get_instance(), APP_MIC_AUDIO_ACTIVE);
                    MessageSendLater(app_get_instance(), APP_MIC_AUDIO_INACTIVE, 0, MIC_AUDIO_INACTIVE_DELAY);
                }
            }
            else if(usb_interface_audio_streaming_speaker == ind->interface)
            {
                USB_RUNDATA.speakerAltsetting = ind->altsetting;

                if (ind->altsetting)
                {
                    MessageCancelAll(app_get_instance(), APP_USB_AUDIO_INACTIVE);
                    MessageSendLater(app_get_instance(), APP_USB_AUDIO_ACTIVE, 0, USB_AUDIO_ACTIVE_DELAY);
                }
                else
                {
                    MessageCancelAll(app_get_instance(), APP_USB_AUDIO_ACTIVE);
                    MessageSendLater(app_get_instance(), APP_USB_AUDIO_INACTIVE, 0, USB_AUDIO_ACTIVE_DELAY);
                }
            }
        }
        break;

        /* message containing the USB audio levels */
        case USB_CLASS_MSG_VOLUME_GOING_UP:
        {
            USB_DEBUG(("USB: USB_CLASS_MSG_VOLUME_GOING_UP\n"));
            usbHidSendMediaCommand(USB_HID_MEDIA_VOL_UP);
        }
        break;

        case USB_CLASS_MSG_VOLUME_GOING_DOWN:
        {
            USB_DEBUG(("USB: USB_CLASS_MSG_VOLUME_GOING_DOWN\n"));
            usbHidSendMediaCommand(USB_CLASS_MSG_VOLUME_GOING_DOWN);
        }
        break;

        /* message indicating that USB has suspended */
        case MESSAGE_USB_SUSPENDED: /* will not be received if bus powered USB device */
        {
            USB_DEBUG(("USB: MESSAGE_USB_SUSPENDED\n"));
            USB_RUNDATA.state = USB_STATE_SUSPENDED;
            MessageSend(app_get_instance(), APP_USB_AUDIO_INACTIVE, 0);
        }
        break;

        case MESSAGE_USB_ATTACHED:
        {
            USB_DEBUG(("USB: MESSAGE_USB_ATTACHED\n"));
            USB_RUNDATA.state = USB_STATE_ATTACHED;
        }
        break;

        case MESSAGE_USB_DETACHED:
        {
            USB_DEBUG(("USB: MESSAGE_USB_DETACHED\n"));
            USB_RUNDATA.state = USB_STATE_DETACHED;
            MessageCancelAll(app_get_instance(), APP_UPDATE_AUDIO_ROUTING);
            MessageSendLater(app_get_instance(), APP_UPDATE_AUDIO_ROUTING, 0, 500);
            MessageSend(app_get_instance(), APP_USB_AUDIO_INACTIVE, 0);
            if(states_get_state() == SOURCE_STATE_POWERED_OFF)
            {
                leds_show_event(LED_EVENT_CHARGER_DETECH);
            }
        }
        break;
        
        case MESSAGE_IMAGE_UPGRADE_ERASE_STATUS:
        case MESSAGE_IMAGE_UPGRADE_COPY_STATUS:
        case MESSAGE_IMAGE_UPGRADE_AUDIO_STATUS:
        case MESSAGE_IMAGE_UPGRADE_HASH_ALL_SECTIONS_UPDATE_STATUS:
        {
            appUpgradeHandleSystemMessages(id, message, FALSE);
        }
        break;

        default:
        {
            USB_DEBUG(("USB: USB MSG Unhandled[0x%x]\n",id));
        }
        break;
    }
}


/****************************************************************************
NAME
    usb_time_critical_init - Enumerate as a USB device before the main application starts up
*/
void usb_time_critical_init(void)
{
    /* must retrieve USB configuration from PS now */
    memset(&USB_RUNDATA, 0, sizeof(USB_DATA_T));
    USB_RUNDATA.usbTask.handler  	  = usb_msg_handler;
    USB_RUNDATA.hostOperatingSystem   = HOST_OS_UNKNOWN;
    USB_RUNDATA.notMacOs              = FALSE;
    USB_RUNDATA.hostMicVolume         = MIC_VOLUME_DEFAULT;
    USB_RUNDATA.hostSpeakerVolume     = SPEAKER_VOLUME_DEFAULT;
    USB_RUNDATA.headsetSpeakerVolume  = HEADSET_VOLUME_DEFAULT;
    USB_RUNDATA.hostSpeakerSampleRate = USB_AUDIO_SAMPLE_RATE_SPEAKER;
    USB_RUNDATA.volumeSyncState       = USB_HID_VOLUME_SYNCED;
    USB_RUNDATA.state                 = USB_STATE_DETACHED;
    USB_RUNDATA.pcVolumeWait          = FALSE;
#ifdef INCLUDE_USB_AUDIO
    /* Add an Audio Control Interface */
    USB_RUNDATA.usb_interface[usb_interface_audio_control] = UsbAddInterface(&usb_codes_ac, B_DESCRIPTOR_TYPE_CS_INTERFACE, usb_descriptor_audio.control.descriptor, usb_descriptor_audio.control.size_descriptor);
    if (USB_RUNDATA.usb_interface[usb_interface_audio_control] == usb_interface_error)
        return;

    VmalMessageSinkTask(StreamUsbClassSink(USB_RUNDATA.usb_interface[usb_interface_audio_control]), &USB_RUNDATA.usbTask);

    /* Add the microphone Audio Streaming Interface */
    USB_RUNDATA.usb_interface[usb_interface_audio_streaming_mic] = UsbAddInterface(&usb_codes_as, B_DESCRIPTOR_TYPE_CS_INTERFACE, usb_descriptor_audio.streaming_mic.descriptor, usb_descriptor_audio.streaming_mic.size_descriptor);

    if (USB_RUNDATA.usb_interface[usb_interface_audio_streaming_mic] == usb_interface_error)
        return;

    /* Add the microphone endpoint */
    if (UsbAddEndPoints(USB_RUNDATA.usb_interface[usb_interface_audio_streaming_mic], 1, usb_descriptor_audio.streaming_mic.end_point_info) == FALSE)
        return;

    VmalMessageSinkTask(StreamUsbClassSink(USB_RUNDATA.usb_interface[usb_interface_audio_streaming_mic]), &USB_RUNDATA.usbTask);

    /* Add the speaker Audio Streaming Interface */
    USB_RUNDATA.usb_interface[usb_interface_audio_streaming_speaker] = UsbAddInterface(&usb_codes_as, B_DESCRIPTOR_TYPE_CS_INTERFACE, usb_descriptor_audio.streaming_speaker.descriptor, usb_descriptor_audio.streaming_speaker.size_descriptor);

    if (USB_RUNDATA.usb_interface[usb_interface_audio_streaming_speaker] == usb_interface_error)
        return;

    /* Add the microphone endpoint */
    if (UsbAddEndPoints(USB_RUNDATA.usb_interface[usb_interface_audio_streaming_speaker], 1, usb_descriptor_audio.streaming_speaker.end_point_info) == FALSE)
        return;

    VmalMessageSinkTask(StreamUsbClassSink(USB_RUNDATA.usb_interface[usb_interface_audio_streaming_speaker]), &USB_RUNDATA.usbTask);

    StreamConfigure(VM_STREAM_USB_ALT_IF_MSG_ENABLED, 1);
#endif

#ifdef INCLUDE_USB_HID
    USB_RUNDATA.usb_interface[usb_interface_hid_consumer_transport] = UsbAddInterface(&usb_codes_hid_no_boot,
                                                                    USB_B_DESCRIPTOR_TYPE_HID,
                                                                    usb_descriptor_hid_consumer_transport.interface.descriptor,
                                                                    usb_descriptor_hid_consumer_transport.interface.size_descriptor);

    if (USB_RUNDATA.usb_interface[usb_interface_hid_consumer_transport] == usb_interface_error)
    {
        USB_DEBUG(("USB: Failed to add hid interface\n"));
        return;
    }

    /* Register HID Datalink Control Device report descriptor with the interface */
    if (UsbAddDescriptor(USB_RUNDATA.usb_interface[usb_interface_hid_consumer_transport],
                        USB_B_DESCRIPTOR_TYPE_HID_REPORT,
                        usb_descriptor_hid_consumer_transport.report.descriptor,
                        usb_descriptor_hid_consumer_transport.report.size_descriptor) == FALSE)
    {
        USB_DEBUG(("USB: Failed to add hid_datalink descriptor\n"));
        return;
    }

    if (UsbAddEndPoints(USB_RUNDATA.usb_interface[usb_interface_hid_consumer_transport],
                        1,
                        usb_descriptor_hid_consumer_transport.interface.end_point_info) == FALSE)
    {
        USB_DEBUG(("USB: Failed to add hid_datalink end points\n"));
        return;
    }

    VmalMessageSinkTask(StreamUsbClassSink(USB_RUNDATA.usb_interface[usb_interface_hid_consumer_transport]), &USB_RUNDATA.usbTask);
#endif

#ifdef INCLUDE_TEAMS
    UsbAddStringDescriptor(UCQ_STRING_INDEX, UsbUcqString);
#endif

    StreamConfigure(VM_STREAM_USB_ATTACH_MSG_ENABLED, 1);
    /**
     *  \Register a task to handle system-wide messages.
     *
     *   Currently the system-wide messages are:
     *   - \#MESSAGE_USB_ENUMERATED
     *   - \#MESSAGE_USB_SUSPENDED
     *   - \#MESSAGE_USB_DECONFIGURED
     *   - \#MESSAGE_USB_ALT_INTERFACE
     *   - \#MESSAGE_USB_ATTACHED
     *   - \#MESSAGE_USB_DETACHED
     *   - \#MESSAGE_PSFL_FAULT
     *   - \#MESSAGE_TX_POWER_CHANGE_EVENT
     *   - \#MESSAGE_SD_MMC_INSERTED
     *   - \#MESSAGE_SD_MMC_REMOVED
     */
    (void)MessageSystemTask(&USB_RUNDATA.usbTask);

    usb_attach();
}


/****************************************************************************
NAME
    usb_get_speaker_source - Get speaker Source
*/
Source usb_get_speaker_source(void)
{
    return StreamUsbEndPointSource(usb_descriptor_audio.streaming_speaker.end_point_info->bEndpointAddress);
}


/****************************************************************************
NAME
    usb_get_mic_sink - Get mic Sink
*/
Sink usb_get_mic_sink(void)
{
    return StreamUsbEndPointSink(usb_descriptor_audio.streaming_mic.end_point_info->bEndpointAddress);
}
void usb_send_media_hid_command(avc_operation_id op_id, bool state)
{
    USB_DEBUG(("USB Set Media HID cmd[0x%X] state[%d]\n", op_id, state));

    switch (op_id)
    {
        case opid_play:
        {
            if (!state)
            {
                usbHidSendMediaCommand(USB_HID_MEDIA_PLAY_PAUSE);
            }
        }
        break;

        case opid_stop:
        {
            if (!state)
            {
                usbHidSendMediaCommand(USB_HID_MEDIA_PAUSE);
            }
        }
        break;

        case opid_pause:
        {
            if (!state)
            {
                usbHidSendMediaCommand(USB_HID_MEDIA_PLAY_PAUSE);
            }
        }
        break;

        case opid_forward:
        {
            if (!state)
            {
                usbHidSendMediaCommand(USB_HID_MEDIA_NEXT_TRACK);
            }
        }
        break;

        case opid_backward:
        {
            if (!state)
            {
                usbHidSendMediaCommand(USB_HID_MEDIA_PREVIOUS_TRACK);
            }
        }
        break;

        default:
        {
        }
        break;
    }
}

#if 0
static void UtilBusyWait(uint16 millisecs)
{
  uint32 time;

  /* spinning the wheels while we wait */
  time = VmGetClock();
  while((VmGetClock() - time) < millisecs);
}
#endif

/****************************************************************************
NAME
    usb_send_media_hid_command - Get USB media command to host
*/
void usbHidSendMediaCommand(USB_HID_MEDIA_CMD_T mediaControl)
{
    uint8 report[2];
    uint8 size_report = 2;

    switch(mediaControl)
    {
    case USB_HID_MEDIA_VOL_UP:
        report[0] = LSB(256); /* key press */
        report[1] = MSB(256); /* key press */
        usbHidSendInputReport(USB_CONSUMER_REPORT_ID, size_report, report);

        report[0] = 0; /* key released */
        report[1] = 0; /* key released */
        usbHidSendInputReport(USB_CONSUMER_REPORT_ID, size_report, report);
        USB_RUNDATA.pcVolumeWait = TRUE;
        break;
    case USB_HID_MEDIA_VOL_DOWN:
        report[0] = LSB(512); /* key press */
        report[1] = MSB(512); /* key press */
        usbHidSendInputReport(USB_CONSUMER_REPORT_ID, size_report, report);

        report[0] = 0; /* key released */
        report[1] = 0; /* key released */
        usbHidSendInputReport(USB_CONSUMER_REPORT_ID, size_report, report);
        USB_RUNDATA.pcVolumeWait = FALSE;
        break;
    case USB_HID_MEDIA_PAUSE:
        report[0] = 0x20; /* key code */
        report[1] = 0; /* key code */
        usbHidSendInputReport(USB_CONSUMER_REPORT_ID, size_report, report);

        report[0] = 0; /* key released */
        report[1] = 0; /* key released */
        usbHidSendInputReport(USB_CONSUMER_REPORT_ID, size_report, report);
        break;
    case USB_HID_MEDIA_STOP:
        report[0] = 0x08; /* key code */
        report[1] = 0;
        usbHidSendInputReport(USB_CONSUMER_REPORT_ID, size_report, report);

        report[0] = 0; /* key released */
        report[1] = 0; /* key released */
        usbHidSendInputReport(USB_CONSUMER_REPORT_ID, size_report, report);
        break;
    case USB_HID_MEDIA_PLAY_PAUSE:
        report[0] = 0x01; /* key code */
        report[1] = 0; /* key code */
        usbHidSendInputReport(USB_CONSUMER_REPORT_ID, size_report, report);

        report[0] = 0; /* key released */
        report[1] = 0; /* key released */
        usbHidSendInputReport(USB_CONSUMER_REPORT_ID, size_report, report);
        break;
    case USB_HID_MEDIA_PLAY:
        report[0] = 0x10; /* key code */
        report[1] = 0; /* key code */
        usbHidSendInputReport(USB_CONSUMER_REPORT_ID, size_report, report);

        report[0] = 0; /* key released */
        report[1] = 0; /* key released */
        usbHidSendInputReport(USB_CONSUMER_REPORT_ID, size_report, report);
        break;
    case USB_HID_MEDIA_NEXT_TRACK:
        report[0] = 0x02; /* key code */
        report[1] = 0; /* key code */
        usbHidSendInputReport(USB_CONSUMER_REPORT_ID, size_report, report);

        report[0] = 0; /* key released */
        report[1] = 0; /* key released */
        usbHidSendInputReport(USB_CONSUMER_REPORT_ID, size_report, report);
        break;
    case USB_HID_MEDIA_PREVIOUS_TRACK:
        report[0] = 0x04; /* key code */
        report[1] = 0; /* key code */
        usbHidSendInputReport(USB_CONSUMER_REPORT_ID, size_report, report);

        report[0] = 0; /* key released */
        report[1] = 0; /* key released */
        usbHidSendInputReport(USB_CONSUMER_REPORT_ID, size_report, report);
        break;
    default:
        break;
    }

}


/****************************************************************************
NAME
    usb_get_hid_mode - Get USB HID mode
*/
USB_HID_MODE_T usb_get_hid_mode(void)
{
    return USB_RUNDATA.hid_mode;
}


/****************************************************************************
NAME
    usb_set_hid_mode - Set USB HID mode
*/
void usb_set_hid_mode(USB_HID_MODE_T mode)
{
    USB_RUNDATA.hid_mode = mode;
    USB_DEBUG(("USB Set Mode [%d]\n", mode));
}

bool usb_stream_out_is_active(void)
{
    return USB_RUNDATA.speakerAltsetting?TRUE:FALSE;
}

bool usb_stream_in_is_active(void)
{
    return USB_RUNDATA.micAltsetting?TRUE:FALSE;
}

bool usbtHostSpeakerIsMute(void)
{
    return USB_RUNDATA.hostSpeakerMute;
}
/****************************************************************************
NAME
    usb_send_telephony_cmd - Sends a HID command to operate a call
*/

bool usb_send_telephony_cmd(USB_HID_TELEPHONY_CMD_T cmd, bool value)
{
    uint8 report[USB_TELEPHONY_REPORT_SIZE];

    USB_DEBUG(("USB: Send Telephony Report (Report ID = %d)\n", USB_TELEPHONY_REPORT_ID));
    /* clear report data */
    memset(report, 0, USB_TELEPHONY_REPORT_SIZE);

    switch(cmd)
    {
        case USB_HID_TELEPHONY_CMD_HOOK_SWITCH:
            if(value)
            {
                USB_RUNDATA.inputTelephonyReport |= USB_HID_TELEPHONY_HOOK_SWITCH;
            }
            else
            {
                USB_RUNDATA.inputTelephonyReport &= ~USB_HID_TELEPHONY_HOOK_SWITCH;
            }

            /* initialise report */
            report[0] = (uint8)(USB_RUNDATA.inputTelephonyReport & 0xFF);
            report[1] = (uint8)(USB_RUNDATA.inputTelephonyReport >> 8 & 0xFF);

            (usbHidSendInputReport(USB_TELEPHONY_REPORT_ID, USB_TELEPHONY_REPORT_SIZE, report));
            break;
        case USB_HID_TELEPHONY_CMD_PHONE_MUTE:
            if(value)
            {
                USB_RUNDATA.inputTelephonyReport |= USB_HID_TELEPHONY_PHONE_MUTE;

                report[0] = (uint8)(USB_RUNDATA.inputTelephonyReport & 0xFF);
                report[1] = (uint8)(USB_RUNDATA.inputTelephonyReport >> 8 & 0xFF);

                (usbHidSendInputReport(USB_TELEPHONY_REPORT_ID, USB_TELEPHONY_REPORT_SIZE, report));


                USB_RUNDATA.inputTelephonyReport &= ~USB_HID_TELEPHONY_PHONE_MUTE;

                /* initialise report */
                report[0] = (uint8)(USB_RUNDATA.inputTelephonyReport & 0xFF);
                report[1] = (uint8)(USB_RUNDATA.inputTelephonyReport >> 8 & 0xFF);

                (usbHidSendInputReport(USB_TELEPHONY_REPORT_ID, USB_TELEPHONY_REPORT_SIZE, report));
            }
            break;
        case USB_HID_TELEPHONY_CMD_FLASH:
            if(value)
            {
                USB_RUNDATA.inputTelephonyReport |= USB_HID_TELEPHONY_FLASH;
                /* initialise report */
                report[0] = (uint8)(USB_RUNDATA.inputTelephonyReport & 0xFF);
                report[1] = (uint8)(USB_RUNDATA.inputTelephonyReport >> 8 & 0xFF);

                (usbHidSendInputReport(USB_TELEPHONY_REPORT_ID, USB_TELEPHONY_REPORT_SIZE, report));

                USB_RUNDATA.inputTelephonyReport &= ~USB_HID_TELEPHONY_FLASH;
                /* initialise report */
                report[0] = (uint8)(USB_RUNDATA.inputTelephonyReport & 0xFF);
                report[1] = (uint8)(USB_RUNDATA.inputTelephonyReport >> 8 & 0xFF);

                (usbHidSendInputReport(USB_TELEPHONY_REPORT_ID, USB_TELEPHONY_REPORT_SIZE, report));
            }
            break;
        case USB_HID_TELEPHONY_CMD_REDIAL:
            if(value)
            {
                USB_RUNDATA.inputTelephonyReport |= USB_HID_TELEPHONY_REDIAL;
                /* initialise report */
                report[0] = (uint8)(USB_RUNDATA.inputTelephonyReport & 0xFF);
                report[1] = (uint8)(USB_RUNDATA.inputTelephonyReport >> 8 & 0xFF);

                (usbHidSendInputReport(USB_TELEPHONY_REPORT_ID, USB_TELEPHONY_REPORT_SIZE, report));

                USB_RUNDATA.inputTelephonyReport &= ~USB_HID_TELEPHONY_REDIAL;
                /* initialise report */
                report[0] = (uint8)(USB_RUNDATA.inputTelephonyReport & 0xFF);
                report[1] = (uint8)(USB_RUNDATA.inputTelephonyReport >> 8 & 0xFF);

                (usbHidSendInputReport(USB_TELEPHONY_REPORT_ID, USB_TELEPHONY_REPORT_SIZE, report));
            }
            break;
        case USB_HID_TELEPHONY_CMD_NONE:
            if(value)
            {
                USB_RUNDATA.inputTelephonyReport = 0;
                /* initialise report */
                report[0] = (uint8)(USB_RUNDATA.inputTelephonyReport & 0xFF);
                report[1] = (uint8)(USB_RUNDATA.inputTelephonyReport >> 8 & 0xFF);

                (usbHidSendInputReport(USB_TELEPHONY_REPORT_ID, USB_TELEPHONY_REPORT_SIZE, report));
            }
            break;
        default:
            return FALSE;
    }

    return TRUE;
}


void usbSendVendorCommandLocalAddr(bdaddr bd_addr)
{
    uint8 report_bytes[USB_DATA_LINK_REPORT_SIZE];

    /* clear report data */
    memset(report_bytes, 0, USB_DATA_LINK_REPORT_SIZE);

    report_bytes[0] = USB_DEVICE_EVENT_LOCAL_ADDRESS;

    report_bytes[1] = (uint8)((bd_addr.nap >> 8) & 0xFF);
    report_bytes[2] = (uint8)(bd_addr.nap & 0xFF);
    report_bytes[3] = bd_addr.uap;
    report_bytes[4] = (uint8)((bd_addr.lap >> 16) & 0xFF);
    report_bytes[5] = (uint8)((bd_addr.lap >> 8) & 0xFF);
    report_bytes[6] = (uint8)(bd_addr.lap & 0xFF);

    USB_DEBUG(("USB: Send local bd_addr\n"));

    /* send USB Report */
    (usbHidSendInputReport(USB_DATA_LINK_REPORT_ID, USB_DATA_LINK_REPORT_SIZE, report_bytes));
}



void usbSendVendorCommandLinkKey(bdaddr bd_addr, uint16 *link_key, uint16 size_link_key)
{
    uint8 report_bytes[USB_DATA_LINK_REPORT_SIZE];
    uint8 i;

    /* clear report data */
    memset(report_bytes, 0, USB_DATA_LINK_REPORT_SIZE);

    report_bytes[0] = USB_DEVICE_EVENT_LINK_KEY_OF_LAST_CONNECTED_DEVICE;

    report_bytes[1] = (uint8)((bd_addr.nap >> 8) & 0xFF);
    report_bytes[2] = (uint8)(bd_addr.nap & 0xFF);
    report_bytes[3] = bd_addr.uap;
    report_bytes[4] = (uint8)((bd_addr.lap >> 16) & 0xFF);
    report_bytes[5] = (uint8)((bd_addr.lap >> 8) & 0xFF);
    report_bytes[6] = (uint8)(bd_addr.lap & 0xFF);

    report_bytes[7] = size_link_key;

    for(i = 0;i < size_link_key; i++)
    {
        report_bytes[8 + 2*i + 1] = link_key[i] & 0xFF;
        report_bytes[8 + 2*i]     = (link_key[i] & 0xFF00) >> 8;
    }

    USB_DEBUG(("USB: Send link key of last conencted device\n"));

    /* send USB Report */
    (usbHidSendInputReport(USB_DATA_LINK_REPORT_ID, USB_DATA_LINK_REPORT_SIZE, report_bytes));
}



bool usbSendVendorCommandSerialNumber(uint16 *sn, uint16 sn_size)
{
    uint8 report_bytes[USB_DATA_LINK_REPORT_SIZE];
    uint8 i;

    /* clear report data */
    memset(report_bytes, 0, USB_DATA_LINK_REPORT_SIZE);

    report_bytes[0] = USB_DEVICE_EVENT_SERIAL_NUMBER;
    report_bytes[1] = (uint8)sn_size * 2;

    for(i = 0;i < sn_size; i++)
    {
        report_bytes[2 + 2*i]     = (sn[i] & 0xFF00) >> 8;
        report_bytes[2 + 2*i + 1] = sn[i] & 0xFF;
    }

    USB_DEBUG(("USB: Send serial number\n"));

    /* send USB Report */
    (usbHidSendInputReport(USB_DATA_LINK_REPORT_ID, USB_DATA_LINK_REPORT_SIZE, report_bytes));

    return TRUE;
}



bool usbSendVendorCommandInquiryResult(bdaddr addr, int16 rssi, uint8 *deviceName, uint8 deviceNameLen)
{
    uint8 report_bytes[USB_DATA_LINK_REPORT_SIZE];
    uint8 device_name_size;

    /* clear report data */
    memset(report_bytes, 0, USB_DATA_LINK_REPORT_SIZE);

    report_bytes[0] = USB_DEVICE_EVENT_INQUIRY_RESULT;

    report_bytes[1] = (uint8)((addr.nap >> 8) & 0xFF);
    report_bytes[2] = (uint8)(addr.nap & 0xFF);
    report_bytes[3] = addr.uap;
    report_bytes[4] = (uint8)((addr.lap >> 16) & 0xFF);
    report_bytes[5] = (uint8)((addr.lap >> 8) & 0xFF);
    report_bytes[6] = (uint8)(addr.lap & 0xFF);

    report_bytes[7] = (uint8)((((uint16)rssi) >> 8) & 0xFF);
    report_bytes[8] = (uint8)(((uint16)rssi) & 0xFF);

    /* initialise report */
    if (deviceNameLen > (USB_DATA_LINK_REPORT_SIZE - sizeof(bdaddr) - 2))
    {
        device_name_size = USB_DATA_LINK_REPORT_SIZE - sizeof(bdaddr) - 2;
    }
    else
    {
        device_name_size = deviceNameLen;
    }

    report_bytes[9] = device_name_size;

    memcpy(&report_bytes[10], deviceName, device_name_size);

    USB_DEBUG(("USB: Send Inquiry Result\n"));

    /* send USB Report */
    (usbHidSendInputReport(USB_DATA_LINK_REPORT_ID, USB_DATA_LINK_REPORT_SIZE, report_bytes));

    return TRUE;
}



bool usbSendVendorCommandConnectedDeviceInfo(bdaddr addr, uint8 *name, uint8 nameSize)
{
	uint8 report_bytes[USB_DATA_LINK_REPORT_SIZE];

    if (usb_get_hid_mode() != USB_HID_MODE_HOST)
    {
        /* only send USB message to Host if it is connected */
        return FALSE;
    }

    USB_DEBUG(("USB: Send connected device information size : %d %s\n", nameSize, name));

    /* clear report data */
    memset(report_bytes, 0, USB_DATA_LINK_REPORT_SIZE);

    report_bytes[0] = USB_HOST_COMMAND_CONNECTED_DEVICE;

    report_bytes[1] = (uint8)((addr.nap >> 8) & 0xFF);
    report_bytes[2] = (uint8)(addr.nap & 0xFF);
    report_bytes[3] = addr.uap;
    report_bytes[4] = (uint8)((addr.lap >> 16) & 0xFF);
    report_bytes[5] = (uint8)((addr.lap >> 8) & 0xFF);
    report_bytes[6] = (uint8)(addr.lap & 0xFF);

    report_bytes[7] = nameSize;

    if(name && nameSize)
    {
        memcpy(&report_bytes[8], name, nameSize);
    }

    /* send USB Report */
    usbHidSendInputReport(USB_DATA_LINK_REPORT_ID, USB_DATA_LINK_REPORT_SIZE, report_bytes);

    return TRUE;
}



bool usbSendVendorCommandLastConnectedDeviceInfo(bdaddr addr, uint8 *name, uint8 nameSize, bool connected)
{
    uint8 report_bytes[USB_DATA_LINK_REPORT_SIZE];

    USB_DEBUG(("USB: Send connected device information size : %d %s\n", nameSize, name));

    if (usb_get_hid_mode() != USB_HID_MODE_HOST)
    {
        /* only send USB message to Host if it is connected */
        return FALSE;
    }

    /* clear report data */
    memset(report_bytes, 0, USB_DATA_LINK_REPORT_SIZE);

    report_bytes[0] = USB_DEVICE_EVENT_LAST_CONNECTED_DEVICE;

    if(BdaddrIsZero(&addr))
    {
        report_bytes[7] = connected?1:0;
        report_bytes[8] = 0;
    }
    else
    {
        report_bytes[1] = (uint8)((addr.nap >> 8) & 0xFF);
        report_bytes[2] = (uint8)(addr.nap & 0xFF);
        report_bytes[3] = addr.uap;
        report_bytes[4] = (uint8)((addr.lap >> 16) & 0xFF);
        report_bytes[5] = (uint8)((addr.lap >> 8) & 0xFF);
        report_bytes[6] = (uint8)(addr.lap & 0xFF);

        report_bytes[7] = connected?1:0;

        report_bytes[8] = nameSize;

        memcpy(&report_bytes[9], name, nameSize);
    }
    /* send USB Report */
    (usbHidSendInputReport(USB_DATA_LINK_REPORT_ID, USB_DATA_LINK_REPORT_SIZE, report_bytes));

    return TRUE;
}



static bool usbSendVendorCommandAck(void)
{
    uint8 report_bytes[USB_DATA_LINK_REPORT_SIZE];

    if (usb_get_hid_mode() != USB_HID_MODE_HOST)
    {
        /* only send USB message to Host if it is connected */
        return FALSE;
    }

    /* clear report data */
    memset(report_bytes, 0, USB_DATA_LINK_REPORT_SIZE);

    /* Response ACK */
    report_bytes[0] = 0xFF;

    USB_DEBUG(("USB: Send ACK\n"));

    /* send USB Report */
    (usbHidSendInputReport(USB_DATA_LINK_REPORT_ID, USB_DATA_LINK_REPORT_SIZE, report_bytes));

    return TRUE;
}



bool usbSendVendorCommandRemoteDeviceDisconnected(void)
{
    uint8 report_bytes[USB_DATA_LINK_REPORT_SIZE];

    USB_DEBUG(("USB: Remote device disconnected\n"));
#if 0
    if (usb_get_hid_mode() != USB_HID_MODE_HOST)
    {
        /* only send USB message to Host if it is connected */
        return FALSE;
    }
#endif
    /* clear report data */
    memset(report_bytes, 0, USB_DATA_LINK_REPORT_SIZE);

    report_bytes[0] = USB_DEVICE_EVENT_REMOTE_DEVICE_DISCONNECTED;

    /* send USB Report */
    (usbHidSendInputReport(USB_DATA_LINK_REPORT_ID, USB_DATA_LINK_REPORT_SIZE, report_bytes));

    return TRUE;
}

/****************************************************************************
NAME
    usb_get_speaker_sample_rate - Gets the configured USB sample rate for the speaker
*/
uint32 usb_get_speaker_sample_rate(void)
{
    return USB_AUDIO_SAMPLE_RATE_SPEAKER;
}

void usbSetDeviceSpeakerVolume(uint8 volume)
{
    USB_DEBUG(("USB: Set Device Volume: Volume = %d\n", volume));
#ifdef INCLUDE_USB_AUDIO
    if(!USB_RUNDATA.state != USB_STATE_DETACHED)
    {
        return;
    }

    if(USB_RUNDATA.headsetSpeakerVolume > volume)
    {
        USB_RUNDATA.volumeSyncState = USB_HID_VOLUME_GOING_DOWN;
        //MessageSend(&USB_RUNDATA.usbTask, USB_CLASS_MSG_VOLUME_GOING_DOWN, NULL);
        usbHidSendMediaCommand(USB_HID_MEDIA_VOL_DOWN);
    }
    else if(USB_RUNDATA.headsetSpeakerVolume < volume)
    {
        USB_RUNDATA.volumeSyncState = USB_HID_VOLUME_GOING_UP;
        //MessageSend(&USB_RUNDATA.usbTask, USB_CLASS_MSG_VOLUME_GOING_UP, NULL);
        usbHidSendMediaCommand(USB_HID_MEDIA_VOL_UP);
    }
    else
    {
        USB_RUNDATA.volumeSyncState = USB_HID_VOLUME_SYNCED;
    }
#endif
    USB_RUNDATA.headsetSpeakerVolume = volume;
}

void usb_detach(void)
{
    UsbHubDetach();
}

void usb_attach(void)
{
    UsbHubAttach();
}

USB_STATE_T usb_get_state(void)
{
    return USB_RUNDATA.state;
}

uint8 usbGetHeadsetSpeakerVolume(void)
{
    return USB_RUNDATA.headsetSpeakerVolume;
}

uint32 usbGetHostSpeakerRate(void)
{
    return USB_RUNDATA.hostSpeakerSampleRate;
}

void sourceUsbSendAudioCodec(uint8 seid)
{
    uint8 report_bytes[USB_DATA_LINK_REPORT_SIZE];

    if (usb_get_hid_mode() == USB_HID_MODE_HOST)
    {
        /* clear report data */
        memset(report_bytes, 0, USB_DATA_LINK_REPORT_SIZE);

        report_bytes[0] = USB_HOST_COMMAND_AUDIO_CODEC;
        report_bytes[1] = 2;
        report_bytes[2] = seid;
        report_bytes[3] = 0; /*Quality*/
        /* send USB Report */
        (usbHidSendInputReport(USB_DATA_LINK_REPORT_ID, USB_DATA_LINK_REPORT_SIZE, report_bytes));
    }
}

bool sourceUsbCanVolUp(void)
{
    if(USB_RUNDATA.hostSpeakerVolume < ((int16)SPEAKER_VOLUME_MAX))
    {
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

bool sourceUsbCanVolDown(void)
{
    if(USB_RUNDATA.hostSpeakerVolume > ((int16)SPEAKER_VOLUME_MIN))
    {
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

/** Get the device volume from the host volume
  * @param[in] hostVolume Volume specified in the host scale
  * @return volume specified in the device scale
  */
static uint16 usbHeadsetSpeakerScaledVolumeGet(int16 hostVolume)
{
  uint16 deviceVolume;

  if(USB_RUNDATA.hostOperatingSystem == HOST_OS_WINDOWS_7 && USB_RUNDATA.notMacOs)
  {
    /* use Win7 scale */
    for (deviceVolume=0; deviceVolume < (sizeof(SpeakerVolumeTableWindowsIn)/sizeof(SpeakerVolumeTableWindowsIn[0])); deviceVolume++)
    {
        if (hostVolume <= SpeakerVolumeTableWindowsIn[deviceVolume])
        {
          break;
        }
    }
  }
  else
  {
    /* Assume linear scale */
    deviceVolume = (uint16)(hostVolume - SPEAKER_VOLUME_MIN + SPEAKER_VOLUME_RES/2)/SPEAKER_VOLUME_RES;
  }

  return deviceVolume;
}


/** Get the host volume from the device volume
  * @param[in] deviceVolume Volume specified in the device scale
  * @return volume specified in the host scale
  */
#if 0
static int16 UsbHostSpeakerVolumeGet(uint16 deviceVolume)
{
  int16 hostVolume;

  if(USB_RUNDATA.hostOperatingSystem == HOST_OS_WINDOWS_7)
  {
    /* use Win7 scale */
    if (deviceVolume >= sizeof(SpeakerVolumeTableWin7Out)/sizeof(SpeakerVolumeTableWin7Out[0]))
    {
      deviceVolume = sizeof(SpeakerVolumeTableWin7Out)/sizeof(SpeakerVolumeTableWin7Out[0]) - 1;
    }

    hostVolume = SpeakerVolumeTableWin7Out[deviceVolume];
  }
  else
  {
    /* Assume linear scale */
    hostVolume = ((deviceVolume * SPEAKER_VOLUME_RES) + SPEAKER_VOLUME_RES);
  }

  return hostVolume;
}
#endif


